﻿using Emgu.CV;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace yysAuxiliary.测试
{
    /// <summary>
    /// 人脸识别帮助类
    /// </summary>
    public class FaceDetectHelper
    {
        private string faceSamplesPath;
        private CascadeClassifier faceClassifier;
        private TrainedFaceRecognizer tfr;

        public FaceDetectHelper(string samplePath = null, string haarPath = null)
        {
            if (string.IsNullOrEmpty(samplePath))
            {
                this.faceSamplesPath = AppDomain.CurrentDomain.BaseDirectory + "s";
            }
            else
            {
                faceSamplesPath = samplePath;
            }
            if (string.IsNullOrEmpty(haarPath))
            {
                //AppDomain.CurrentDomain.BaseDirectory +
                faceClassifier = new CascadeClassifier(@"F:\xiagao\摄像头\摄像头\bin\Debug\haarcascade_frontalface_default.xml");
            }
            else
            {
                faceClassifier = new CascadeClassifier(haarPath);
            }

            SetTrainedFaceRecognizer(FaceRecognizerType.EigenFaceRecognizer);
        }

        /// <summary>
        /// 设置识别器
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public TrainedFaceRecognizer SetTrainedFaceRecognizer(FaceRecognizerType type)
        {
            tfr = new TrainedFaceRecognizer();
            tfr.trainedFileList = GetSampleFacesList();

            switch (type)
            {
                case FaceRecognizerType.EigenFaceRecognizer:
                    tfr.faceRecognizer = new Emgu.CV.Face.EigenFaceRecognizer(80, double.PositiveInfinity);
                    break;
                case FaceRecognizerType.FisherFaceRecognizer:
                    tfr.faceRecognizer = new Emgu.CV.Face.FisherFaceRecognizer(80, 3500);
                    break;
                case FaceRecognizerType.LBPHFaceRecognizer:
                    tfr.faceRecognizer = new Emgu.CV.Face.LBPHFaceRecognizer(1, 8, 8, 8, 100);
                    break;
            }
            tfr.faceRecognizer.Train(tfr.trainedFileList.trainedImages.ToArray(), tfr.trainedFileList.trainedLabelOrder.ToArray());
            return tfr;
        }

        /// <summary>
        /// 获取训练结果
        /// </summary>
        /// <returns></returns>
        private TrainedFileList GetSampleFacesList()
        {
            TrainedFileList tf = new TrainedFileList();
            DirectoryInfo di = new DirectoryInfo(faceSamplesPath);
            int i = 0;
            foreach (FileInfo fi in di.GetFiles())
            {
                tf.trainedImages.Add(new Image<Gray, byte>(fi.FullName));
                tf.trainedLabelOrder.Add(i);
                tf.trainedFileName.Add(fi.Name.Split('_')[0]);
                i++;
            }
            return tf;
        }

        /// <summary>
        /// 获取图片中的人脸矩形
        /// </summary>
        /// <param name="emguImage"></param>
        /// <returns></returns>
        private FaceDetectedObj GetFaceRectangle(Mat emguImage)
        {
            FaceDetectedObj fdo = new FaceDetectedObj();
            fdo.originalImg = emguImage;
            List<Rectangle> faces = new List<Rectangle>();
            try
            {
                using (UMat ugray = new UMat())
                {
                    CvInvoke.CvtColor(emguImage, ugray, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);//灰度化图片
                    CvInvoke.EqualizeHist(ugray, ugray);//均衡化灰度图片

                    Rectangle[] facesDetected = faceClassifier.DetectMultiScale(ugray, 1.1, 10, new Size(20, 20));
                    faces.AddRange(facesDetected);
                }
            }
            catch (Exception ex)
            {                
                throw ex;
                //todo
            }
            fdo.facesRectangle = faces;

            return fdo;
        }

        /// <summary>
        /// 人脸识别
        /// </summary>
        /// <param name="emguImage"></param>
        /// <returns></returns>
        public FaceDetectedObj FaceRecognize(Mat emguImage)
        {
            FaceDetectedObj fdo = GetFaceRectangle(emguImage);
            Image<Gray, byte> tempImg = fdo.originalImg.ToImage<Gray, byte>();
            #region 画矩形边框
            using (Graphics g = Graphics.FromImage(fdo.originalImg.Bitmap))
            {
                foreach (Rectangle face in fdo.facesRectangle)
                {
                    g.DrawRectangle(new Pen(Color.Red, 2), face);//给识别出的人脸画矩形框

                    Image<Gray, byte> grayFace = tempImg.Copy(face).Resize(100, 100, Emgu.CV.CvEnum.Inter.Cubic);//灰度化[size必须和样本size一致]
                    grayFace._EqualizeHist();//得到均衡化人脸的灰度图像

                    #region 匹配姓名
                    Emgu.CV.Face.FaceRecognizer.PredictionResult pr = tfr.faceRecognizer.Predict(grayFace);//根据灰度图片从训练器中识别人脸
                    string recogniseName = tfr.trainedFileList.trainedFileName[pr.Label].ToString();

                    string name = tfr.trainedFileList.trainedFileName[pr.Label].ToString();

                    Font font = new Font("宋体", 16, GraphicsUnit.Pixel);
                    SolidBrush fontLine = new SolidBrush(Color.Yellow);
                    float xPos = face.X + (face.Width / 2 - (name.Length * 14) / 2);
                    float yPos = face.Y - 21;
                    g.DrawString(name, font, fontLine, xPos, yPos);
                    #endregion

                    fdo.names.Add(name);
                }
            }
            #endregion
            return fdo;
        }

        /// <summary>
        /// 添加人脸样本
        /// </summary>
        /// <param name="emguImage"></param>
        /// <param name="faceName"></param>
        public void AddSampleFace(Mat emguImage, string faceName)
        {
            string filePath = faceSamplesPath + "/" + faceName + "_" + System.Guid.NewGuid().ToString() + ".png";
            emguImage.Save(filePath);
        }

        /// <summary>
        /// base64转Bitmap
        /// </summary>
        /// <param name="base64"></param>
        /// <returns></returns>
        public Bitmap Base64ToBitmap(string base64)
        {
            if (base64.StartsWith("data:image/jpeg;base64,"))
            {
                base64 = base64.Remove(0, "data:image/jpeg;base64,".Length);
            }
            byte[] b = Convert.FromBase64String(base64);
            MemoryStream ms = new MemoryStream(b);
            Bitmap bm = new Bitmap(ms);
            return bm;
        }

        /// <summary>
        /// Bitmap转Mat
        /// </summary>
        /// <param name="bm"></param>
        /// <returns></returns>
        public Mat BitmapToMat(Bitmap bm)
        {
            Mat image = new Mat();
            Image<Bgr, Byte> currentFrame = new Image<Bgr, Byte>(bm);
            CvInvoke.BitwiseAnd(currentFrame, currentFrame, image);
            return image;
        }
    }

    /// <summary>
    /// 人脸样本集合
    /// </summary>
    public class TrainedFileList
    {
        /// <summary>
        /// 样本图片
        /// </summary>
        public List<Image<Gray, byte>> trainedImages = new List<Image<Gray, byte>>();

        /// <summary>
        /// 识别顺序（EmguCv 3.* 只返回识别序号）
        /// </summary>
        public List<int> trainedLabelOrder = new List<int>();

        /// <summary>
        /// 样本名字
        /// </summary>
        public List<string> trainedFileName = new List<string>();
    }

    /// <summary>
    /// 训练好的人脸识别器
    /// </summary>
    public class TrainedFaceRecognizer
    {
        public Emgu.CV.Face.FaceRecognizer faceRecognizer;

        /// <summary>
        /// 训练样本集合
        /// </summary>
        public TrainedFileList trainedFileList;
    }

    /// <summary>
    /// 识别后的相关信息
    /// </summary>
    public class FaceDetectedObj
    {
        /// <summary>
        /// 原图片
        /// </summary>
        public Mat originalImg;

        /// <summary>
        /// 矩形信息
        /// </summary>
        public List<Rectangle> facesRectangle;

        /// <summary>
        /// 人脸名字
        /// </summary>
        public List<string> names = new List<string>();
    }

    /// <summary>
    /// 识别器枚举
    /// </summary>
    public enum FaceRecognizerType
    {
        EigenFaceRecognizer = 0,
        FisherFaceRecognizer = 1,
        LBPHFaceRecognizer = 2,
    }

}
